#include "Logic.h"
using namespace std;

/* getLegalMoves:
 * used to iterate through a player's hand, and uses the rules construct to
 * whether a card is legal or not. Then pushes it onto a list of legal cards,
 * and returns that list.
 */
list<Card> Logic::getLegalMoves(int pNum){
        list<Card>::iterator i;
        list<Card> legalCards;
        for(i = players[pNum]->cArray.begin(); i != players[pNum]->cArray.end(); i++){
                if(rules->isLegal(*(i))){
                        legalCards.push_back(*(i));
                }
        }
        return legalCards;
}

/* listInitCard:
 * used on the first turn to only produce a list of the starting card instead
 * of all legal moves.
 */
list<Card> Logic::listInitCard(int pNum){
        list<Card>::iterator i;
        list<Card> legalCards;
        for(i = players[pNum]->cArray.begin(); i != players[pNum]->cArray.end(); i++){
                if(rules->isStartCard(*(i))){
                                legalCards.push_back(*(i));
                                }
        }
        return legalCards;
}

                
                
/* getStartPlayer():
 * get the initial starting player by looping through a player's hand and
 * finding the startcard according to the rule type we are using.
 * Return -1 if we fail epicly
 */
int Logic::getStartPlayer(){
        int startp;
        list<Card>::iterator i;
        for(int pNum = 0; pNum < 4; pNum++){
                for(i = players[pNum]->cArray.begin(); i != players[pNum]->cArray.end(); i++){
                        if(rules->isStartCard(*(i))){
                                startp = pNum;
                                return startp;
                        }
                }
        }
        return -1;
}

/* startGame:
 * This function is used to rotate between player turns and allow them to play
 * the game. We first get the initial starting player, and then loop around
 * until players have no cards left
 */
void Logic::startGame(){
        //Variables
        
        Command command;
        int startp;
        bool flag = true;
        
        //Get the starting player
        startp = this->getStartPlayer();
        
        //While a player has cards, keep playing
        cout << "A new round begins. It's player " << startp+1 << "'s turn to play." << endl;
        while(!(players[0]->cArray.empty()) || !(players[1]->cArray.empty()) || !(players[2]->cArray.empty()) || !(players[3]->cArray.empty())){

                //Do not initialize startp, we need to start at the player who
                //has the startCard
                for(; startp < 4; startp++){
                        //We'll let playerturn check for invalid commands
                        //Get the command and execute it
                        if(players[startp]->cArray.empty()) break;
                        if(!flag){
                                command = players[startp]->playerturn(rules->playedClubs, rules->playedDiamonds, rules->playedHearts, rules->playedSpades, getLegalMoves(startp), allCards);
                        }
                        else if(flag){
                                command = players[startp]->playerturn(rules->playedClubs,rules->playedDiamonds, rules->playedHearts, rules->playedSpades, listInitCard(startp), allCards);
                                flag = false;
                        }
                        

                        executeCommand(command, startp);
                        if(command.type == RAGEQUIT) startp--;
                }
                //Reset startp to 0
                startp = 0;
        }
}

/* executeCommand:
 * This function will actually execute any commands passed back to the Logic
 * structure. This assumes that the commands will actually work.
 * We will use the player structure to test for illegal moves.
 */
void Logic::executeCommand(Command command, int pNum){
        list<Card>::iterator i;
        //If we are playing a card
        if(command.type == PLAY){
                //Erase the card from the list
                for(i = players[pNum]->cArray.begin(); i != players[pNum]->cArray.end(); i++){
                        if(i->suit_ == command.card.suit_ && i->rank_ == command.card.rank_){
                                players[pNum]->cArray.erase(i);
                                break;
                        }
                }
                //Set the previous card to that card
                rules->prevCard = command.card;
                //Add the card to the list of played Cards
                if(command.card.suit_ == CLUB){
                        if(!rules->playedClubs.empty()){
                                for(i = rules->playedClubs.begin(); i != rules->playedClubs.end(); i++){
                                        if(i->rank_ > command.card.rank_){
                                                rules->playedClubs.insert(i,command.card);
                                                break;
                                        }
                                }
                                if(i == rules->playedClubs.end()){
                                        rules->playedClubs.push_back(command.card);
                                }
                        }
                        else rules->playedClubs.push_front(command.card);
                }
                else if(command.card.suit_ == DIAMOND){
                        if(!rules->playedDiamonds.empty()){

                                for(i = rules->playedDiamonds.begin(); i != rules->playedDiamonds.end(); i++){
                                        if(i->rank_ > command.card.rank_){
                                                rules->playedDiamonds.insert(i,command.card);
                                                break;
                                        }
                                }
                                if(i == rules->playedDiamonds.end()){
                                        rules->playedDiamonds.push_back(command.card);
                                }
                        }
                        else rules->playedDiamonds.push_front(command.card);
                }

                else if(command.card.suit_ == HEART){
                        if(!rules->playedHearts.empty()){
                                
                                for(i = rules->playedHearts.begin(); i != rules->playedHearts.end(); i++){
                                        if(i->rank_ > command.card.rank_){ //i->rank_ > command.card.rank_){
                                                rules->playedHearts.insert(i,command.card);
                                                break;
                                        }
                                }
                                if(i == rules->playedHearts.end()){
                                        rules->playedHearts.push_back(command.card);
                                }
                        }
                        else rules->playedHearts.push_front(command.card);

                }
                else if(command.card.suit_ == SPADE){
                        if(!rules->playedSpades.empty()){
                                for(i = rules->playedSpades.begin(); i != rules->playedSpades.end(); i++){
                                        if(i->rank_ > command.card.rank_){
                                                rules->playedSpades.insert(i,command.card);
                                                break;
                                        }
                                }
                                if(i == rules->playedSpades.end()){
                                        rules->playedSpades.push_back(command.card);
                                }
                        }
                        else rules->playedSpades.push_front(command.card);
                }

        }
        //If we are discarding the card
        if(command.type == DISCARD){
                //Find the card, and push it onto the discard
                for(i = players[pNum]->cArray.begin(); i != players[pNum]->cArray.end(); i++){
                        if(i->suit_ == command.card.suit_ && i->rank_ == command.card.rank_){
                                players[pNum]->cDiscard.push_back(*(i));
                                players[pNum]->cArray.erase(i);
                                break;
                        }
                }
        }
        //We don't have to handle this, Player does
        if(command.type == DECK){
                this->printDeck();
        }
        //Exit
        if(command.type == QUIT){
                exit(0);
        }
        //rageQUIT
        if(command.type == RAGEQUIT){
                Player *p = new cPlayer();
                p->setPNum(pNum+1);
                p->setPScore(players[pNum]->getPScore());
                p->cArray = players[pNum]->cArray;
                p->cDiscard = players[pNum]->cDiscard;
                delete players[pNum];
                players[pNum] = p;
        }

}

void Logic::printDeck(){
        for(int i = 0; i < 52; i++){
                cout << allCards[i];
                if( (i + 1) % 13 == 0 )cout << endl;
                else cout << " ";
        }
}

/* assignScores:
 * Print out the discards of each player and assign their scores accordingly
 */
void Logic::assignScores(){
        //Score counter
        int score;
        //Loop through the players
        for(int pNum = 0; pNum < 4; pNum++){
                score = 0;
                cout << "Player " << pNum+1 << "'s discards:";
                //Loop through the respective player's discards
                for(list<Card>::iterator i = players[pNum]->cDiscard.begin(); i != players[pNum]->cDiscard.end(); i++){
                        
                        score += i->rank_+1;
                        cout << " ";
                        cout << *(i);
                        
                        
                }
                cout << endl;
                cout << "Player " << pNum+1 << "'s score: " << players[pNum]->getPScore() << " + " << score;
                players[pNum]->setPScore(score + players[pNum]->getPScore());
                cout << " = " << players[pNum]->getPScore() << endl;
        }
}
/* emptyDiscards:
 * Remove all the played cards and each player's respective discards
 */
void Logic::emptyDiscards(){
        for(int pNum = 0; pNum < 4; pNum++){
                players[pNum]->cDiscard.clear();
        }
                rules->playedClubs.clear();
                rules->playedDiamonds.clear();
                rules->playedHearts.clear();
                rules->playedSpades.clear();
}
